模块一:函数式编程与JS异步编程、手写Promise

简答题

谈谈你是如何理解JS异步编程的，EventLoop、消息队列都是做什么的，什么是宏任务，什么是微任务?

答：
异步编程：由于js是单线程运行，所以都是排队执行任务，如果中间遇见特别耗时的任务就只能等待，导致整个程序阻塞产生假死的现象。为了避免这种现象，引入了异步模式，将耗时的任务额外放在一边执行，到执行完成达到某种条件后才从队列中放回线程执行。
EventLoop、消息队列：Eventloop是一个程序结构，用于等待和发送消息和事件。在程序中有两个线程：一个负责程序本身的运行，即主线程；另外一个负责主线程和其他进程的通讯，即“Eventloop"。程序执行过程中会把任务按顺序放入队列中依次排队执行。
宏任务：宿主环境（常见的有浏览器和node)提供的任务，常见的宏任务有setTimeout, setInterval.
微任务：语言标准提供的任务。常见的有Promise回调函数,Object.observe。


代码题

一、将下面异步代码使用Promise的方式改进

setTimeout( function () {var a ='hello'

setTimeout (function () {var b = 'lagou'

setTimeout (function () {varc='Ivu'

console.log(a + b + C)

}，10)

}，10)

},10)


改进后：

function p1 () {
    return new Promise(function (resolve, reject) {
      setTimeout(() => {
          resolve('hello')
      },10)
    })
}
  
function p2 (value) {
    let data = value + ' lagou'
    return new Promise(function (resolve, reject) {
        setTimeout((value) => resolve(data),10)
    })
}

function p3(value) {
    let data = value + ' Ivu'
    return new Promise(function(resolve,reject) {
        setTimeout((value) => resolve(data),10)
    })
}
  
p1().then(p2).then(p3).then(function(value){
    console.log(value)
})

二、基于以下代码完成下面的四个练习

练习1:

rerurn fp.flowRight(fp.prop('in_stock'),fp.last(cars))

练习2:

retrun fp.flowRight(fp.prop('name'),fp.first(cars))

练习3:

let averageDollarValue = fp.flowRight(_average,fp.map(car => car.dollar_value))

练习4:

let sanitizeNames = fp.flowRight(_underscore,fp.toLower)

三、基于下面提供的代码，完成后续的四个练习

练习1:使用fp.add(x, y)和fp.map(f, x)创建一一个能让functor里的值增加的函数ex1

let ex1 = (maybe,y) => {
    return maybe.map(x => fp.map(fp.add(y),maybe._value))
}

练习2:实现-一个函数ex2，能够使用fp.first获取列表的第一一个元素

let ex2 = () => {
    return fp.first(xs._value)
}

练习3:实现一个函数ex3,使用safeProp和fp.first找到user的名字的首字母

let ex3 = () => {
    return fp.first(safeProp('name',user)._value)
}

练习4:使用Maybe重写ex4，不要有if语句

let ex4 = n => {
    let maybe = Maybe.of(n).map(x => parseInt(x))
    return maybe._value
}

四、手写实现MyPromise源码
要求:尽可能还原Promise中的每一一个API,并通过注释的方式描述思路和原理。


1.Promise 就是一个会立即执行的类。
2.核心是通过两个参数函数更改状态：成功fulfilled、失败reject、等待pending.状态一旦确定便不更改。
3.then方法的作用：通过判断状态，来分别执行不同的回调函数
4.then中成功的回调函数参数是value,失败回调函数参数reason.回调函数的参数定义成实例属性，初始值为undefined
5.异步逻辑：成功和失败的回调函数不立即执行，需要等待状态不是等待中时才执行，所以需要先将两种回调函数存储起来。
6.then可链式调用，返回的都是Promise对象。通过promise对象的状态来决定链式的下一个执行哪个回调函数。所以应该将回调函数以数组存储。在返回的promse对象中，通过回调函数把值传递给下一个then方法。
7.promise的then方法传递的值或者Promse对象成功之后回调函数的值.如果是普通值，就直接默认是resolve状态，下个then方法执行回调执行，传过来的值就是上个方法传过来的普通值。如果是Promise对象，就传递对象的值。即：成功时传递成功回调执行的结果值，失败传递失败回调。
8.Promise对象的then方法中调用promise对象自身，会报系统错误Chaining cyccle detected for promise。
9.执行器执行过程中报错，需要抛出异常,通过reject执行传递错误信息。
10.resolve回调函数执行中报错也需要通知reject执行传递抛出错误。
11.then方法的resolve和reject回调函数或者等待上一个结果异步时，发生的错误都一样要抛出错误。都通过try和catch捕获错误.
12.then方法的参数可选。不传递参数时，相当于方法传递的函数就是x => x，直接将函数传递给下一个方法。
13.all方法实现异步操作按照调用的顺序得到执行之后的结果。all方法返回的也是promise对象。
14.all方法是promise类的方法，所以应该定义为静态方法。且只有调用的对象执行结果都为resolve时返回值才是resolve.
15.resolve方法(将值转化成Promise对象并返回)：参数是值得话，会创建一个promise对象，并把值包裹在对象中，将promise对象作为返回值。如果参数是Promise对象，就将promise对象原封不动作为返回值返回。
16.finally方法：无论当前状态成功或者失败，都将执行方法中的回调函数。并且finally方法之后也可以链式调用，所以还需要考虑方法中回调函数是否也有异步的情况。最简便的方法是也通过promise对象的resolve处理，以便保持执行顺序,然后在then方法中把值再return。
17.then方法中也可以不定义失败回调，而直接通过catch方法直接捕获.直接在catch方法中注册失败回调即可。

//状态定义为常量，便于复用
const PENDING = 'pending' //等待
const FULFILLED = 'fulfilled' //成功
const REJECTED = 'rejected' //失败
class MyPromise{ 
    constructor(executor){
        try{
            executor(this.resolve,shis.reject)
        } catch(e){
            this.reject(e)
        }

    }
    //初始状态为处理
    status = PENDING
    //成功之后的值
    value = undefined
    //失败之后的原因
    reason = undefined
    //成功之后的回调
    successCallback = []
    //失败回调
    failCallback = []
    resolve = (value) => {
        //状态已确定，即不为等待时，不更改状态，直接return退出
        if(this.status !== PENDING) return;
        //状态更改为成功
        this.status = FULFILLED
        //保存成功之后的值
        this.value = value
        //判断回调是否存在，存在便调用
        while(this.successCallback.length) this.successCallback.shift()()
    }
    reject = (reason) => {
        //状态已确定，即不为等待时，不更改状态，直接return退出
        if(this.status !== PENDING) return;
        //状态更改为失败
        this.status = REJECTED
        //保存失败的原因
        this.reason = reason
        //判断是否有失败回调，存在便调用
        while(this.failCallback.length) this.failCallback().shift()()
    }
    then (successCallback,failCallback){
        successCallback = successCallback ? successCallback : value => value;
        failCallback = failCallback ?failCallback: reason => {throw this.reason}
        let promise2 = new MyPromise((resolve,reject) => {
            //判断状态
            if(this.status === FULFILLED) {
                //resolve回调需要等待上一个链式then方法返回状态为resolve时才执行，所以需要借助setTimeout转成异步执行
                setTimeout(() => {
                    try {
                        let x = successCallback(this.value);
                        //给下个then方法传递x值。首先要判断x是普通值还会Promise对象。
                        //如果是普通值，直接调用resolve,如果是promise对象，查看对象返回的结果。再根据结果调用对应的回调函数
                        resolvePromise(x,resolve,reject)
                    } catch (e) {
                        reject(e)
                    }

                },0)
            }else if (this.status === REJECTED){
                setTimeout(() => {
                    try {
                        let x = failCallback(this.reason);
                        //给下个then方法传递x值。首先要判断x是普通值还会Promise对象。
                        //如果是普通值，直接调用resolve,如果是promise对象，查看对象返回的结果。再根据结果调用对应的回调函数
                        resolvePromise(x,resolve,reject)
                    } catch (e) {
                        reject(e)
                    }

                },0)
            }else{
                //将成功回调和失败回调存储起来
                this.successCallback.push(() => {
                    setTimeout(() => {
                        try {
                            let x = successCallback(this.value);
                            //给下个then方法传递x值。首先要判断x是普通值还会Promise对象。
                            //如果是普通值，直接调用resolve,如果是promise对象，查看对象返回的结果。再根据结果调用对应的回调函数
                            resolvePromise(x,resolve,reject)
                        } catch (e) {
                            reject(e)
                        }
    
                    },0)
                })
                this.failCallback.push(() => {
                    setTimeout(() => {
                        try {
                            let x = failCallback(this.reason);
                            //给下个then方法传递x值。首先要判断x是普通值还会Promise对象。
                            //如果是普通值，直接调用resolve,如果是promise对象，查看对象返回的结果。再根据结果调用对应的回调函数
                            resolvePromise(x,resolve,reject)
                        } catch (e) {
                            reject(e)
                        }
    
                    },0)
                })
            }
        })
        //返回Promise对象
        return promise2
    }

    finally(callback){
        return this.then(value => {
            return MyPromise.resolve(callback()).then(() => value)
        },reason => {
            return MyPromise.resolve(callback()).then(() => {throw reason})
        })
    }

    catch(failCallback){
        return this.then(undefined,failCallback)
    }

    static all(array){
        let result = [];
        let index = 0;
        return new MyPromise((resolve,reject) => {
            function addData(key,value){
                result[key] = value
                index++;
                if(index === array.length){
                    //因为有些promise对象是异步操作，直接循环之后运行，可能还未获取到对象执行的返回值。
                    //所以通过判断result的长度来确保所有all方法的参数都已执行完成添加到result中之后再执行reaolve；
                    resolve(result)
                }
            }
            for(let i = 0;i<array.length;i++){
                let current = array[i]
                if(current instanceof MyPromise){
                    //promise对象直接执行。成功回调将值添加到结果数组，失败回调
                    current.then(value => addData(i,value),reason => reject(reason))
                }else {
                    //普通值
                    addData(i,array[i])
                }
            }
        })
    }

    static resolve(value){
        if(value instanceof MyPromise){
            return value
        }else{
            return new MyPromise(resolve => this.resolve(value))
        }
    }
}

function resolvePromise(x,resolve,reject) {
    if(promise2 === x){
        return reject(new TypeError('Chaining cyccle detected for promise #<Promise>'))
    }
    if ( x instanceof MyPromise) {
        x.then(resolve,reject)
    }else {
        resolve(x)
    }
}